package com.jl.utils;

import com.alibaba.fastjson.JSON;
import com.github.pagehelper.util.StringUtil;
import com.jl.controller.SpiderController;
import lombok.extern.slf4j.Slf4j;
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.edge.EdgeDriver;
import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;

import java.util.*;

/**
 * @author : ljl
 * @date : 8:51 2021/1/8
 */
@Slf4j
public class WebDriverUtils {

    /**
     * 初始化无头模式谷歌游览器
     *
     * @param driver
     * @param path
     * @return
     */
    public static WebDriver initHeadleChromeDriver(WebDriver driver, String path) {
        driver.close();
        System.setProperty("webdriver.chrome.driver", path);//本地路径

        ArrayList<String> arg = new ArrayList<>();
        arg.add("enable-automation");
        //初始化一个谷歌浏览器实例，实例名称叫driver
        ChromeOptions chromeOptions = new ChromeOptions()
                .addArguments("--window-size=1920,1080")
                .addArguments("--start-maximized")
                //设置 chrome 的无头模式
                .addArguments("--headless")
                .addArguments("--user-agent=" + UserAgentUtils.getRandomAgent())
//                //设置Chrome启动时的参数,忽略证书（SSL）错误
                .addArguments("--ignore-certificate-errors")
//                //开启一个实验性参数excludeSwitches，用来隐藏window.navigator.webdriver返回true,这个参数必须是List
                .setExperimentalOption("excludeSwitches", arg)
                .addArguments("--disable-gpu")
                .addArguments("--disable-extensions")
                .addArguments("--proxy-bypass-list=*");
        return new ChromeDriver(chromeOptions);
    }

    /**
     * 初始化谷歌游览器
     *
     * @return
     */
    public static WebDriver initChromeDriver(String path) {
        System.setProperty("webdriver.chrome.driver", path);//本地路径
        ArrayList<String> arg = new ArrayList<>();
        arg.add("enable-automation");
        String agent = UserAgentUtils.getRandomAgent();
        log.info("==========agent：{}", agent);
        //初始化一个谷歌浏览器实例，实例名称叫driver
        ChromeOptions chromeOptions = new ChromeOptions()
                //设置 chrome 的无头模式
//                .addArguments("--headless")
                .addArguments("--user-agent=" + agent)
//                .addArguments("--proxy-server=http://","49.70.85.85:9999")
                //设置Chrome启动时的参数,忽略证书（SSL）错误
                .addArguments("--ignore-certificate-errors")
//                //开启一个实验性参数excludeSwitches，用来隐藏window.navigator.webdriver返回true,这个参数必须是List
                .setExperimentalOption("excludeSwitches", arg)
//                .addArguments("--window-size=1920,1080")
                .addArguments("--disable-gpu")
                .addArguments("--disable-extensions")
                .addArguments("--proxy-server='direct://'")
//                .addArguments("--start-maximized")
                .addArguments("--proxy-bypass-list=*");
        return new ChromeDriver(chromeOptions);
    }

    /**
     * 初始化 ege 游览器
     *
     * @return
     */
    public static WebDriver initEdgeDriver() {
        //初始化Edge 游览器
        System.setProperty("webdriver.edge.driver", "spider-server/tools/MicrosoftWebDriver.exe");
        return new EdgeDriver();
    }

    /**
     * 动态代理
     *
     * @return
     */
    public static DesiredCapabilities proxyIp() {
        String proxyIpAndPort = "36.248.132.201" + ":" + "9999";
        DesiredCapabilities capabilities = DesiredCapabilities.chrome();
        Proxy proxy = new Proxy();
        proxy.setHttpProxy(proxyIpAndPort).setFtpProxy(proxyIpAndPort).setSslProxy(proxyIpAndPort);
        capabilities.setCapability(CapabilityType.ForSeleniumServer.AVOIDING_PROXY, true);
        capabilities.setCapability(CapabilityType.ForSeleniumServer.ONLY_PROXYING_SELENIUM_TRAFFIC, true);
        capabilities.setCapability(CapabilityType.PROXY, proxy);
//        WebDriver  driver = new ChromeDriver(capabilities);;
        return capabilities;
    }

    /**
     * 登录
     *
     * @param driver
     * @param loginUrl
     * @return
     * @throws Exception
     */
    public static WebDriver login(WebDriver driver, String loginUrl) throws Exception {
        driver.get(loginUrl);
        while (isElementExist(driver, By.id("ERROR"))) {
            driver.quit();
            SpiderController spiderController = new SpiderController();
            spiderController.start();
        }
        JavascriptExecutor js = (JavascriptExecutor) driver;
        // 登录时需执行的js
        js.executeScript("Object.defineProperty(navigator,\"webdriver\",{get:()=>undefined,});");
        // 设置温馨提示语
        WebElement p = driver.findElement(By.className("login-ft")).findElement(By.tagName("p"));
        js.executeScript("arguments[0].innerHTML= '<span style=\"color:red;font-size:x-large;\">爬虫程序提醒您请60s内登录成功</span>';", p);
        Set<Cookie> cookies = driver.manage().getCookies();
        return driver;
    }

    /**
     * 查询任务
     *
     * @param driver
     * @return
     * @throws Exception
     */
    public static WebDriver seekTicket(final WebDriver driver, final String startSite, final String endSite, final String startDate, final int monthType) throws Exception {
        // 输入出发地
        WebElement webElement = driver.findElement(By.id("fromStationText"));
        webElement.clear();
        webElement.click();
        webElement.sendKeys(startSite);

        // 获取搜索出来的出发地
        webElement = driver.findElement(By.id("panel_cities"));
        List<WebElement> webElements = webElement.findElements(By.tagName("div"));
        if (webElements.size() > 0) {
            // 点击选择出发地
            webElements.get(0).click();
            webElements.clear();
        }

        // 输入目的地
        webElement = driver.findElement(By.id("toStationText"));
        webElement.clear();
        webElement.click();
        webElement.sendKeys(endSite);

        // 获取搜索出来的目的地
        webElement = driver.findElement(By.id("panel_cities"));
        webElements = webElement.findElements(By.tagName("div"));
        if (webElements.size() > 0) {
            // 点击选择出发地
            webElements.get(0).click();
            webElements.clear();
        }

        // 点击日期框
        driver.findElement(By.id("train_date")).click();

        //获取日期集合 [0]小月份 [1]大月份， 如 1月 2月
        webElements = driver.findElements(By.className("cal-cm"));

        //获取出发月份数据
        webElement = webElements.get(monthType);

        //获取出发月份内日期
        List<WebElement> elementCells = webElement.findElements(By.className("cell"));

        //点击选中出发日期
        elementCells.get(Integer.parseInt(startDate) - 1).click();

        //选择高铁
        driver.findElement(By.name("cc_type")).click();

        //点击查询
        driver.findElement(By.id("query_ticket")).click();
        Thread.sleep(2000);

        return driver;
    }

    /**
     * 获取车票列表
     *
     * @param driver
     * @return
     */
    public static List<WebElement> getTicketList(final WebDriver driver) {
        // 获得车票列表
        WebElement webElement = driver.findElements(By.id("queryLeftTable")).get(0);
        List<WebElement> webElements = webElement.findElements(By.tagName("tr"));
        List<WebElement> webElementList = new ArrayList<>();
        for (int i = 1; i <= webElements.size(); i++) {
            if (i % 2 != 0) {
                webElementList.add(webElements.get(i - 1));
            }
        }
        return webElementList;

    }

    /**
     * 寻找车票
     *
     * @param j
     * @param webElementsTd
     * @param driver
     * @throws Exception
     */
    public static void seekTickets(final int j, final List<WebElement> webElementsTd, final WebDriver driver) throws Exception {
        Map<String, String> map = new LinkedHashMap<>();
        for (int i = 0; i < webElementsTd.size(); i++) {
            try {
                switch (i) {
                    case 0:
                        //获取车次
                        WebElement web = webElementsTd.get(i).findElement(By.tagName("a"));
                        map.put("车次", web.getText());

                        //获取出发站、到达站
                        List<WebElement> webElementls = webElementsTd.get(i).findElement(By.className("cdz")).findElements(By.tagName("strong"));
                        map.put("出发站", webElementls.get(0).getText());
                        map.put("到达站", webElementls.get(1).getText());

                        //获取出发时间、到达时间
                        webElementls = webElementsTd.get(i).findElement(By.className("cds")).findElements(By.tagName("strong"));
                        map.put("出发时间", webElementls.get(0).getText());
                        map.put("到达时间", webElementls.get(1).getText());

                        //获取行车时长
                        webElementls = webElementsTd.get(i).findElement(By.className("ls")).findElements(By.tagName("strong"));
                        map.put("历时", webElementls.get(0).getText());
                        break;
                    case 1:
                        map.put("商务座/特等座", webElementsTd.get(i).getText());
                        break;
                    case 2:
                        map.put("一等座", webElementsTd.get(i).getText());
                        break;
                    case 3:
                        map.put("二等座/二等包座", webElementsTd.get(i).getText());
                        break;
                    case 10:
                        map.put("无座", webElementsTd.get(i).getText());
                        break;
                    case 11:
                        map.put("其他", webElementsTd.get(i).getText());
                        break;
                    case 12:
                        map.put("备注", webElementsTd.get(i).getText());
                        // 车次
                        String mumber = webElementsTd.get(0).findElement(By.tagName("a")).getText();

                        if (isWebElementExist(webElementsTd.get(i), By.tagName("a"))) {
                            // 多线程找票, 同步代码块排队购票
//                            synchronized (webElementsTd.get(i)) {
                            log.info("==========【{}：开始购票】：{}", j + 1, JSON.toJSONString(map));
                            /** 无头模式下不能点击预订 */
                            WebElement webElementA = webElementsTd.get(i).findElement(By.tagName("a"));
                            webElementA.click();


//                            String js = "document.getElementsByClassName(\""+webElementA.getAttribute("class")+"\")[0].style.display='inline-block';";
//                            ((JavascriptExecutor)driver).executeScript(js);
//                            Thread.sleep(2000);
//                            webElementA.click();

//                            JavascriptExecutor js = (JavascriptExecutor) driver;
//                            js.executeScript("arguments[0].click();", webElementsTd.get(i).findElement(By.tagName("a")));

//                            driver.findElement(By.linkText(webElementA.getText())).click();

//                            String js = "document.getElementsByClassName(\""+webElementA.getAttribute("class")+"\")[0].children[0].click()";
//                            ((JavascriptExecutor)driver).executeScript(js);

                            //创建鼠标属性方法
//                            Actions action=new Actions(driver);
//                            // 获取 moveToElement 方法 ，元素定位到想要移上去的元素上
//                            action.moveToElement(webElementA).perform();
//                            Thread.sleep(2000);
//                            action.click();


                            log.info("=====u1:{}", driver.getCurrentUrl());
                            Thread.sleep(5000);
                            log.info("=====u2:{}", driver.getCurrentUrl());
                            if (isDisplay(driver, By.id("ticket_con_id"))) {
                                //执行购票任务
                                WebDriverUtils.getTickets(driver, mumber);
                            }
//                            }
                        } else {
                            log.info("=========={}：无票车次：{}", j + 1, JSON.toJSONString(map));
                        }
                        map.clear();
                        break;
                    default:
                        break;
                }
            } catch (Exception e) {
                String number = webElementsTd.get(0).findElement(By.tagName("a")).getText();
                log.error("==========车次：{},异常：{}", number, e.getMessage());
            }
        }

    }

    /**
     * 执行购票任务
     *
     * @param driver
     * @param number
     * @return
     * @throws Exception
     */
    public static void getTickets(final WebDriver driver, final String number) throws Exception {
        log.info("=====================车次【{}】车票信息", number);
        WebElement webElement = driver.findElement(By.id("ticket_con_id"));
        List<WebElement> webElements = webElement.findElements(By.tagName("span"));
        WebElement webElementZero = webElements.get(0), webElementTwo = webElements.get(2), webElementFour = webElements.get(4);

        log.info("==========车次【{}】,车票信息：{}", number, webElementZero.getText());
        log.info("==========车次【{}】,车票信息：{}", number, webElementTwo.getText());
        log.info("==========车次【{}】,车票信息：{}", number, webElementFour.getText());

        String level = "二等座";
        switch (level) {
            case "商务座":
                break;
            case "一等座":
                break;
            case "二等座":
                //二等座购票
                secondSeat(driver, webElementFour, level, number);
                break;
            default:
                break;
        }
    }

    /**
     * 二等座购票
     *
     * @param driver
     * @param webElementFour
     * @param level
     * @param number
     * @throws Exception
     */
    public static List<String> secondSeat(WebDriver driver, WebElement webElementFour, String level, String number) throws Exception {
        List<String> names = new ArrayList<>();
        if (webElementFour.getText().indexOf("张") != -1 || webElementFour.getText().indexOf("有") != -1) {
            // 选择乘车人
            names = checkPeople(driver, webElementFour);
            // 切换车票级别
            checkTicket(driver, level);
            // 点击提交订单
            driver.findElement(By.id("submitOrder_id")).click();
            // 选择座位
            checkSeat(driver);
            // 点击确定购票
//        driver.findElement(By.id("confirmDiv")).findElements(By.tagName("a")).get(1).click();
            log.info("=====================购票成功：{}", number);

            //判断系统是否出结果 等待系统购票，这里需要很长时间
//            if (isDisplay(driver,By.id("payButton"))){
//                log.info("=====================下单成功：{}", JSON.toJSONString(names));
//                driver.close();
//            }else {
//                log.info("=====================下单失败：{}", JSON.toJSONString(names));
//                driver.close();
//            }
        } else {
            log.info("=====================该车次【{}】{}无票", number, level);
        }
        // 回退上一页
        driver.navigate().back();
        return names;
    }

    /**
     * 一等座购票
     *
     * @param driver
     * @param webElementFour
     * @param level
     * @param number
     * @throws Exception
     */
    public static void firstSeat(WebDriver driver, WebElement webElementFour, String level, String number) throws Exception {

    }

    /**
     * 商务座购票
     *
     * @param driver
     * @param webElementFour
     * @param level
     * @param number
     * @throws Exception
     */
    public static void businessSeat(WebDriver driver, WebElement webElementFour, String level, String number) throws Exception {

    }

    /**
     * 选择乘车人
     *
     * @param driver
     * @param webElementFour
     * @return
     */
    public static List<String> checkPeople(WebDriver driver, WebElement webElementFour) {
        List<String> names = new ArrayList<>();
        // 有限票数情况,车票信息：一等座（￥374.0）20张票
        if (webElementFour.getText().indexOf("张") != -1) {
            log.info("==========开始购买：{}", webElementFour.getText());
            // 乘车人计数
            int number = -1;
            //车票信息：一等座（￥374.0）20张票
            String seat = webElementFour.getText();
            //二等座车票数量
            int count2 = Integer.parseInt(seat.substring(seat.indexOf("）") + 1, seat.length() - 2));

            List<WebElement> webElements = driver.findElement(By.id("normal_passenger_id")).findElements(By.tagName("li"));
            while (count2 > 0) {
                count2--;
                number++;
                // 乘车人都选择票了就不重复选
                if (webElements.size() < number) {
                    // 选择乘车人
                    webElements.get(number).findElement(By.tagName("input")).click();
                    //获取乘车人姓名
                    String name = webElements.get(number).findElement(By.tagName("label")).getText();
                    names.add(name);
                }
            }
            // 有票情况,车票信息：二等座（￥249.0）有票
        } else if (webElementFour.getText().indexOf("有") != -1) {
            log.info("==========开始购买：{}", webElementFour.getText());
            WebElement webElement1 = driver.findElement(By.id("normal_passenger_id"));
            List<WebElement> webElements = webElement1.findElements(By.tagName("li"));
            webElements.forEach(s -> {
                // 选择乘车人
                s.findElement(By.tagName("input")).click();
                if (isElementExist(driver, By.id("content_defaultwarningAlert_id"))) {
                    driver.findElement(By.id("qd_closeDefaultWarningWindowDialog_id")).click();
                }
                //获取乘车人姓名
                String name = s.findElement(By.tagName("label")).getText();
                names.add(name);
            });
        }
        return names;

    }

    /**
     * 选择座位
     *
     * @param driver
     * @return
     * @throws Exception
     */
    public static WebDriver checkSeat(WebDriver driver) throws Exception {
        if (isDisplay(driver, By.id("content_checkticketinfo_id"))) {
            // 选择座位位置
            List<WebElement> webElements = driver.findElement(By.id("check_ticketInfo_id")).findElements(By.tagName("tr"));
            List<WebElement> uls = driver.findElement(By.id("erdeng1")).findElements(By.tagName("ul"));
            //2个人就选一排2座
            if (webElements.size() == 2) {
                uls.get(1).findElements(By.tagName("li")).get(0).findElement(By.tagName("a")).click();
                Thread.sleep(200);
                uls.get(1).findElements(By.tagName("li")).get(1).findElement(By.tagName("a")).click();
                //3三个人就选一排3座
            } else if (webElements.size() == 3) {
                uls.get(0).findElements(By.tagName("li")).get(0).findElement(By.tagName("a")).click();
                Thread.sleep(200);
                uls.get(0).findElements(By.tagName("li")).get(1).findElement(By.tagName("a")).click();
                Thread.sleep(200);
                uls.get(0).findElements(By.tagName("li")).get(2).findElement(By.tagName("a")).click();
            }
        }
        return driver;
    }


    /**
     * 选择车票等级
     *
     * @param driver
     * @param level  商务座、一等座、二等座
     */
    public static void checkTicket(WebDriver driver, String level) {
        int ticketType = level.equals("商务座") ? 2 : level.equals("一等座") ? 1 : 0;
        WebElement webElement = driver.findElement(By.id("ticketInfo_id"));
        //获取所以乘车人
        List<WebElement> webElements = webElement.findElements(By.tagName("tr"));
        // 获取没有手机号的乘车人
        List<WebElement> lis = driver.findElement(By.id("normal_passenger_id")).findElements(By.tagName("li"));
        List<Integer> index = new ArrayList<>();
        int count = 0;
        for (int i = 0; i < webElements.size(); i++) {
            if (i % 2 == 0) {
                count++;
                webElement = webElements.get(i).findElement(By.id("seatType_" + count));
                webElement.click();
                List<WebElement> ops = webElements.get(i).findElements(By.tagName("option"));
                ops.get(ticketType).click(); //选择二等座

                //获取乘车人的电话
                if (isElementExist(driver, By.id("phone_no_" + count))) {
                    String phone = driver.findElement(By.id("phone_no_" + count)).getAttribute("value");
                    if (StringUtil.isEmpty(phone)) {
                        index.add(i / 2);
                    }
                }
            }
        }
        index.forEach(i -> {
            // 取消没有手机号的乘车人
            lis.get(i).findElement(By.tagName("input")).click();
            if (isElementExist(driver, By.id("content_defaultwarningAlert_id"))) {
                driver.findElement(By.id("qd_closeDefaultWarningWindowDialog_id")).click();
            }
        });
    }

    /**
     * 定位页面
     *
     * @param driver
     * @return
     */
    public static WebDriver switchToDriver(final WebDriver driver) {
        WebDriver driverTwo = null;
        String currentWindow = driver.getWindowHandle();
        Set<String> handles = driver.getWindowHandles();
        Iterator<String> it = handles.iterator();
        while (it.hasNext()) {
            String handle = it.next();
            if (currentWindow.equals(handle)) {
                driverTwo = driver.switchTo().window(handle);
            }
        }
        return driverTwo;
    }


    /**
     * 等待指定元素文本出现
     *
     * @param driver
     * @param cssName
     * @return
     * @throws Exception
     */
    public static Boolean isDisplay(final WebDriver driver, final By cssName) {
        boolean result = false;
        int attempts = 0;
        while (attempts < 3) {
            try {
                attempts++;
                result = new WebDriverWait(driver, 20)
                        .until(new ExpectedCondition<Boolean>() {
                            public Boolean apply(WebDriver driver) {
                                return driver.findElement(cssName) == null ? false : true;
                            }
                        });
                return true;
            } catch (Exception e) {
                log.error("==========第{}次没找到,{}", attempts, e.getMessage());
            }
        }
        return result;
    }

    /**
     * 判断元素是否存在
     *
     * @param driver
     * @return
     */
    public static boolean isElementExist(final WebDriver driver, final By by) {
        try {
            driver.findElement(by);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 判断当前webElement 下 By 属性是否存在
     *
     * @param webElement
     * @param by
     * @return
     */
    public static boolean isWebElementExist(final WebElement webElement, final By by) {
        try {
            webElement.findElement(by);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

}
